package practice.server;

import java.util.LinkedList;
import java.util.Queue;
import java.util.concurrent.atomic.AtomicLong;

/**
 * 手写一个简单的线程池
 * 维护任务队列和工作线程列表
 * 用的是 FIFO
 * 
 * @author insight
 * @since 2021/9/9
 */
public class DefaultThreadPool<Job extends Runnable> implements ThreadPool<Job> {
    private static final int MAX_WORKER_NUM = 16;
    private static final int MIN_WORKER_NUM = 1;
    private static final int DEFAULT_WORKER_NUM = 5;
    
    private final Queue<Job> jobs;
    private final LinkedList<Worker> workers;
    private int workNum;
    private AtomicLong threadNum = new AtomicLong();

    public DefaultThreadPool() {
        this(DEFAULT_WORKER_NUM);
    }

    public DefaultThreadPool(int initialSize) {
        workNum = initialSize > MAX_WORKER_NUM ? MAX_WORKER_NUM :
                Math.max(initialSize, MIN_WORKER_NUM);
        jobs = new LinkedList<>();
        workers = new LinkedList<>();
        initialWorker(workNum);
    }

    @Override
    public void execute(Job job) {
        if (job != null) {
            synchronized (jobs) {
                jobs.offer(job);
                // 每次只唤醒一个 开销更小
                jobs.notify();
            }
        }
    }

    @Override
    public void shutDown() {
        workers.forEach(Worker::shutDown);
    }

    @Override
    public void addWorker(int num) {
        if (num < 0) throw new IllegalArgumentException();
        
        synchronized (workers) {
            int available = MAX_WORKER_NUM - workNum;
            if (num > available) {
                num = available;
            }
            initialWorker(num);
            workNum += num;
        }
    }

    @Override
    public void removeWorker(int num) {
        if (num >= workNum) {
            throw new IllegalArgumentException();
        }

        synchronized (jobs) {
            synchronized (workers) {
                for (int i = 0; i < num; i++) {
                    Worker worker = workers.getFirst();
                    workers.removeFirst();
                    worker.shutDown();
                }
                workNum -= num;
            }
        }
    }

    @Override
    public int getJobCnt() {
        return jobs.size();
    }

    @Override
    public int getWorkerCnt() {
        return workers.size();
    }

    private void initialWorker(int workNum) {
        for (int i = 0; i < workNum; i++) {
            Worker worker = new Worker();
            workers.add(worker);
            new Thread(worker, "Thread-" + threadNum.incrementAndGet()).start();
        }
    }

    private class Worker implements Runnable {
        private volatile boolean running = true;
        
        @Override
        public void run() {
            while (running) {
                Job job;

                synchronized (jobs) {
                    while (jobs.isEmpty()) {
                        try {
                            jobs.wait();
                        } catch (InterruptedException e) {
                            // 可中断
                            Thread.currentThread().interrupt();
                            return ;
                        }
                    }
                    job = jobs.poll();
                }

                if (job != null) {
                    job.run();
                }
            }
        }
        
        public void shutDown() {
            this.running = false;
        }
    }
    
}